/*
 * Copyright 2013 Joan Zapata
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.adpater;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
import android.text.TextWatcher;
import android.text.util.Linkify;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import android.widget.AdapterView;
import android.widget.CheckedTextView;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RatingBar;
import android.widget.TextView;

import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.FloatRange;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.core.text.util.LinkifyCompat;
import androidx.recyclerview.widget.RecyclerView;

import com.android.adpater.interfaces.ImageLoad;

import java.io.File;

/**
 *
 * @author GYL
 * @version 1.0
 * Adapter View Helper 帮助类
 */
@SuppressWarnings({"unused", "WeakerAccess", "UnusedReturnValue"})
public class AdapterViewHelper {

  /**
   * Views indexed with their IDs
   */
  private final SparseArray<View> mViews;
  private final Context mContext;

  private View mConvertView;
  private int mPosition;
  private int mLayoutId;
  private static ImageLoad mImageLoad;

  /**
   * Package private field to retain the associated user object and detect a change
   */
  private Object mAssociatedObject;

  protected AdapterViewHelper(Context context, ViewGroup parent, int layoutId, int position) {
    this.mContext = context;
    this.mPosition = position;
    this.mLayoutId = layoutId;
    this.mViews = new SparseArray<>();
    mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
    mConvertView.setTag(this);
//  if (null != Adapter.singleton && null != Adapter.singleton.getImageLoad()) {
//     mImageLoad = Adapter.singleton.getImageLoad();
//  }
  }


  /**
   * Custom network load images
   */
  public static void setImageLoad(@NonNull ImageLoad imageLoad) {
    mImageLoad = imageLoad;
  }


  /**
   * This method is the only entry point to get a AdapterViewHelper.
   *
   * @param context The current mContext.
   * @param convertView The convertView arg passed to the getView() method.
   * @param parent The parent arg passed to the getView() method.
   * @return A AdapterViewHelper instance.
   */
  public static AdapterViewHelper get(Context context, View convertView, ViewGroup parent, int layoutId) {
    return get(context, convertView, parent, layoutId, -1);
  }

  static AdapterViewHelper get(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
    //
    if (convertView == null) {
      return new AdapterViewHelper(context, parent, layoutId, position);
    }
    // Retrieve the existing helper and update its position
    AdapterViewHelper existingHelper = (AdapterViewHelper) convertView.getTag();
    if (existingHelper.mLayoutId != layoutId) {
      return new AdapterViewHelper(context, parent, layoutId, position);
    }
    existingHelper.mPosition = position;
    return existingHelper;
  }

  /**
   * Retrieve the mConvertView
   */
  public View getView() {
    return mConvertView;
  }

  /**
   * This method allows you to retrieve a view and perform custom operations on it, not covered by the AdapterViewHelper. If you think it's a common use case, please consider creating a new issue at https://github.com/JoanZapata/base-adapter-helper/issues.
   *
   * @param viewId The id of the view you want to retrieve.
   */
  public <T extends View> T getView(@IdRes int viewId) {
    return retrieveView(viewId);
  }

  /**
   * Retrieve the overall mPosition of the mData in the list.
   *
   * @throws IllegalArgumentException If the mPosition hasn't been set at the construction of the this helper.
   */
  public int getPosition() {
    if (mPosition == -1) {
      throw new IllegalStateException("Use AdapterViewHelper constructor " +
          "with mPosition if you need to retrieve the mPosition.");
    }
    return mPosition;
  }

  /**
   * Retrieves the last converted object on this view.
   */
  public Object getAssociatedObject() {
    return mAssociatedObject;
  }

  /**
   * Should be called during convert
   */
  public void setAssociatedObject(Object associatedObject) {
    this.mAssociatedObject = associatedObject;
  }

  /**
   *
   * @param viewId
   * @param drawable
   * @return
   */
  public AdapterViewHelper setBackground(@IdRes int viewId, @NonNull Drawable drawable) {
    return setBackground(retrieveView(viewId),drawable);
  }

  public AdapterViewHelper setBackground(View view, @NonNull Drawable drawable) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
      view.setBackground(drawable);
    }else{
      view.setBackgroundDrawable(drawable);
    }
    return this;
  }

  /**
   * Will set background color of a view.
   *
   * @param viewId The view id.
   * @param color A color, not a resource id.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setBackgroundColor(@IdRes int viewId, @ColorInt int color) {
    retrieveView(viewId).setBackgroundColor(color);
    return this;
  }

  /**
   * Will set background of a view.
   *
   * @param viewId The view id.
   * @param backgroundRes A resource to use as a background.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setBackgroundRes(@IdRes int viewId, @DrawableRes int backgroundRes) {
    retrieveView(viewId).setBackgroundResource(backgroundRes);
    return this;
  }

  public AdapterViewHelper setBackgroundRes(@NonNull View view, @DrawableRes int backgroundRes) {
    view.setBackgroundResource(backgroundRes);
    return this;
  }

  /**
   * Will set the text of a TextView.
   *
   * @param viewId The view id.
   * @param value The text to put in the text view.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setText(@IdRes int viewId, CharSequence value) {
    ((TextView) retrieveView(viewId)).setText(value);
    return this;
  }

  public AdapterViewHelper setTextRes(@IdRes int viewId, @StringRes int resId) {
    ((TextView) retrieveView(viewId)).setText(resId);
    return this;
  }

  /**
   * Will set text color of a TextView.
   *
   * @param viewId The view id.
   * @param textColor The text color (not a resource id).
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setTextColor(@IdRes int viewId, @ColorInt int textColor) {
    ((TextView) retrieveView(viewId)).setTextColor(textColor);
    return this;
  }

  /**
   * Will set text color of a TextView.
   *
   * @param viewId The view id.
   * @param textColorRes The text color resource id.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setTextColorRes(@IdRes int viewId, @ColorRes int textColorRes) {
    //noinspection deprecation
    ((TextView) retrieveView(viewId)).setTextColor(mContext.getResources().getColor(textColorRes));
    return this;
  }

  /**
   * Will set text color of a TextView.
   *
   * @param viewId The view id.
   * @param textColorRes The text color resource id.
   * @param theme theme The theme used to style the color attributes, may be {@code null}.
   * @return The AdapterViewHelper for chaining.
   */
  @TargetApi(Build.VERSION_CODES.M)
  public AdapterViewHelper setTextColorRes(
      @IdRes int viewId, @ColorRes int textColorRes, @Nullable Resources.Theme theme) {
    ((TextView) retrieveView(viewId)).setTextColor(mContext.getResources().getColor(textColorRes, theme));
    return this;
  }

  @RequiresApi(api = Build.VERSION_CODES.M)
  public AdapterViewHelper setImageIcon(@IdRes int viewId, @NonNull Icon icon) {
    ((ImageView) retrieveView(viewId)).setImageIcon(icon);
    return this;
  }

  /**
   * Will set the image of an ImageView from a resource id.
   *
   * @param viewId The view id.
   * @param imageResId The image resource id.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setImageResource(@IdRes int viewId, @DrawableRes int imageResId) {
    if(null != mImageLoad){
      mImageLoad.loadByResource(mContext, (ImageView) retrieveView(viewId),imageResId);
    }else{
      ((ImageView) retrieveView(viewId)).setImageResource(imageResId);
    }
    return this;
  }

  public AdapterViewHelper setImageUri(@IdRes int viewId, @NonNull Uri uri) {
    if(null != mImageLoad){
      mImageLoad.loadByUri(mContext, (ImageView) retrieveView(viewId),uri);
    }
    return this;
  }

  public AdapterViewHelper setImageFile(@IdRes int viewId, @NonNull File file) {
    if(null != mImageLoad){//
      mImageLoad.loadByFile(mContext, (ImageView) retrieveView(viewId),file);
    }
    return this;
  }


  /**
   * Will set the image of an ImageView from a drawable.
   *
   * @param viewId The view id.
   * @param drawable The image drawable.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setImageDrawable(@IdRes int viewId, @NonNull Drawable drawable) {
    ((ImageView) retrieveView(viewId)).setImageDrawable(drawable);
    return this;
  }

  public AdapterViewHelper setImageUrl(@IdRes int viewId, @NonNull String imageUrl,@DrawableRes int defImge) {
    if (null != mImageLoad) {
      ImageView view = retrieveView(viewId);
      mImageLoad.loadByUrl(mContext, view, imageUrl,defImge);
    }
    return this;
  }

  public AdapterViewHelper setImageUrl(@NonNull ImageView imageView, @NonNull String imageUrl,@DrawableRes int defImge) {
    if (null != mImageLoad) {
      mImageLoad.loadByUrl(mContext, imageView, imageUrl,defImge);
    }
    return this;
  }


  /**
   * Will download an image from a URL and put it in an ImageView.
   *
   * @param viewId The view id.
   * @param imageUrl The image URL.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setImageUrl(@IdRes int viewId, @NonNull String imageUrl) {
    if (null != mImageLoad) {
      ImageView view = retrieveView(viewId);
      mImageLoad.loadByUrl(mContext, view, imageUrl);
    }
    return this;
  }
  /**
   * Add an action to set the image of an image view. Can be called multiple times.
   */
  public AdapterViewHelper setImageBitmap(@IdRes int viewId, @NonNull Bitmap bitmap) {
    ((ImageView) retrieveView(viewId)).setImageBitmap(bitmap);
    return this;
  }

  /**
   * Add an action to set the alpha of a view. Can be called multiple times. Alpha between 0-1.
   */
  public AdapterViewHelper setAlpha(@IdRes int viewId, @FloatRange(from = 0.0, to = 1.0) float value) {
    retrieveView(viewId).setAlpha(value);
    return this;
  }

  /**
   * Set a view visibility to VISIBLE (true) or GONE (false).
   *
   * @param viewId The view id.
   * @param visible True for VISIBLE, false for GONE.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setVisible(@IdRes int viewId, boolean visible) {
    retrieveView(viewId).setVisibility(visible ? View.VISIBLE : View.GONE);
    return this;
  }

  public AdapterViewHelper setVisible(@IdRes int viewId, int visibility) {
    retrieveView(viewId).setVisibility(visibility);
    return this;
  }

  public AdapterViewHelper setEnabled(@IdRes int viewId, boolean enabled) {
    retrieveView(viewId).setEnabled(enabled);
    return this;
  }

  public AdapterViewHelper setFocusable(@IdRes int viewId, boolean focusable) {
    retrieveView(viewId).setFocusable(focusable);
    return this;
  }

  public AdapterViewHelper setFocusableInTouchMode(@IdRes int viewId, boolean focusableInTouchMode) {
    retrieveView(viewId).setFocusableInTouchMode(focusableInTouchMode);
    return this;
  }

  /**
   * Add All links into a TextView.
   *
   * @param viewId TextView id.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper addAllLinks(@IdRes int viewId) {
    addLinks(viewId, Linkify.ALL);
    return this;
  }

  /**
   * Add links into a TextView.
   *
   * @param viewId TextView id.
   * @return The AdapterViewHelper for chaining.
   * @see Linkify#addLinks(TextView text, int mask)
   */
  public AdapterViewHelper addLinks(@IdRes int viewId, @LinkifyCompat.LinkifyMask int mask) {
    TextView view = retrieveView(viewId);
    Linkify.addLinks(view, mask);
    return this;
  }

  /**
   * Apply the typeface to the given viewId, and enable sub pixel rendering.
   */
  public AdapterViewHelper setTypeface(@IdRes int viewId, @NonNull Typeface typeface) {
    TextView view = retrieveView(viewId);
    view.setTypeface(typeface);
    view.setPaintFlags(view.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
    return this;
  }

  /**
   * Apply the typeface to all the given viewIds, and enable sub pixel rendering.
   */
  public AdapterViewHelper setTypeface(@NonNull Typeface typeface, @IdRes int... viewIds) {
    for (int viewId : viewIds) {
      setTypeface(viewId, typeface);
    }
    return this;
  }

  public AdapterViewHelper setTypeface(@IdRes int viewId, Typeface typeface, int style) {
    TextView view = retrieveView(viewId);
    view.setTypeface(typeface, style);
    view.setPaintFlags(view.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
    return this;
  }

  /**
   * Sets the progress of a ProgressBar.
   *
   * @param viewId The view id.
   * @param progress The progress.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setProgress(@IdRes int viewId, int progress) {
    ((ProgressBar) retrieveView(viewId)).setProgress(progress);
    return this;
  }

  /**
   * Sets the progress and max of a ProgressBar.
   *
   * @param viewId The view id.
   * @param progress The progress.
   * @param max The max value of a ProgressBar.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setProgress(@IdRes int viewId, int progress, int max) {
    ProgressBar view = retrieveView(viewId);
    view.setMax(max);
    view.setProgress(progress);
    return this;
  }

  /**
   * Sets the range of a ProgressBar to 0...max.
   *
   * @param viewId The view id.
   * @param max The max value of a ProgressBar.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setMax(@IdRes int viewId, int max) {
    ((ProgressBar) retrieveView(viewId)).setMax(max);
    return this;
  }

  /**
   * Sets the rating (the number of stars filled) of a RatingBar.
   *
   * @param viewId The view id.
   * @param rating The rating.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setRating(@IdRes int viewId, float rating) {
    ((RatingBar) retrieveView(viewId)).setRating(rating);
    return this;
  }

  /**
   * Sets the rating (the number of stars filled) and max of a RatingBar.
   *
   * @param viewId The view id.
   * @param rating The rating.
   * @param max The range of the RatingBar to 0...max.
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setRating(@IdRes int viewId, float rating, int max) {
    RatingBar view = retrieveView(viewId);
    view.setMax(max);
    view.setRating(rating);
    return this;
  }

  /**
   * Sets the superCode of the view.
   *
   * @param viewId The view id.
   * @param tag The superCode;
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setTag(@IdRes int viewId, @NonNull Object tag) {
    retrieveView(viewId).setTag(tag);
    return this;
  }

  /**
   * Sets the superCode of the view.
   *
   * @param viewId The view id.
   * @param key The key of superCode;
   * @param tag The superCode;
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setTag(@IdRes int viewId, int key, @NonNull Object tag) {
    retrieveView(viewId).setTag(key, tag);
    return this;
  }

  /**
   * Sets the checked status of a checkable.
   *
   * @param viewId The view id.
   * @param checked The checked status;
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setChecked(@IdRes int viewId, boolean checked) {
    View view = retrieveView(viewId);
    if (view instanceof CompoundButton) {
      ((CompoundButton) view).setChecked(checked);
    } else if (view instanceof CheckedTextView) {
      ((CheckedTextView) view).setChecked(checked);
    }
    return this;
  }

  public AdapterViewHelper setSelected(@IdRes int viewId,boolean selected){
    retrieveView(viewId).setSelected(selected);
    return this;
  }

  /**
   * 添加TextWatcher,避免EditText 输入数据时数据错乱
   * @param editTextId EditText id
   * @param text  内容
   * @param textWatcher TextWatcher
   * @return
   */
  public AdapterViewHelper addTextChangedListener(@IdRes int editTextId,CharSequence text, TextWatcher textWatcher){
    View view=retrieveView(editTextId);
    if(view instanceof EditText ){
      addTextChangedListener((EditText) view,text,textWatcher);
//      EditText editText= (EditText) view;
//      //删除TextWatcher
//      if(view.getTag() instanceof TextWatcher){
//        editText.removeTextChangedListener((TextWatcher) view.getTag());
//      }
//      //设置Text
//      editText.setText(text);
//      //添加TextWatcher
//      ((EditText) view).addTextChangedListener(textWatcher);
//      view.setTag(textWatcher);
    }else{
      Log.e("AdapterViewHelper","AdapterViewHelper(L:591) >>> View 必须是 EditText");
    }
    return this;
  }

  /**
   * 添加TextWatcher,避免EditText 输入数据时数据错乱
   * @param editText EditText id
   * @param text  内容
   * @param textWatcher TextWatcher
   * @return
   */
  public AdapterViewHelper addTextChangedListener(@NonNull EditText editText, CharSequence text,
      TextWatcher textWatcher) {
    //删除TextWatcher
    if (editText.getTag() instanceof TextWatcher) {
      editText.removeTextChangedListener((TextWatcher) editText.getTag());
    }
    //设置Text
    editText.setText(text);
    //添加TextWatcher
    editText.addTextChangedListener(textWatcher);
    editText.setTag(textWatcher);
    return this;
  }

  /**
   * Sets the adapter of a adapter view.
   *
   * @param viewId The view id.
   * @param adapter The adapter;
   * @return The AdapterViewHelper for chaining.
   */
  @SuppressWarnings("unchecked")
  public AdapterViewHelper setAdapter(@IdRes int viewId, @NonNull android.widget.Adapter adapter) {
    ((AdapterView) retrieveView(viewId)).setAdapter(adapter);
    return this;
  }

  public AdapterViewHelper setAdapter(@IdRes int viewId, @NonNull RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) {
    ((RecyclerView) retrieveView(viewId)).setAdapter(adapter);
    return this;
  }

  /**
   * Sets the on click listener of the view.
   *
   * @param viewId The view id.
   * @param listener The on click listener;
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setOnClickListener(@IdRes int viewId, @NonNull View.OnClickListener listener) {
    retrieveView(viewId).setOnClickListener(listener);
    return this;
  }

  /**
   * Sets the on touch listener of the view.
   *
   * @param viewId The view id.
   * @param listener The on touch listener;
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setOnTouchListener(@IdRes int viewId, @NonNull View.OnTouchListener listener) {
    retrieveView(viewId).setOnTouchListener(listener);
    return this;
  }

  /**
   * Sets the on long click listener of the view.
   *
   * @param viewId The view id.
   * @param listener The on long click listener;
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setOnLongClickListener(@IdRes int viewId, @NonNull View.OnLongClickListener listener) {
    retrieveView(viewId).setOnLongClickListener(listener);
    return this;
  }

  /**
   * Sets the ListView or GridView's item click listener of the view
   *
   * @param viewId The view id.
   * @param listener The item on click listener;
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setOnItemClickListener(@IdRes int viewId,
      @NonNull AdapterView.OnItemClickListener listener) {
    ((AdapterView) retrieveView(viewId)).setOnItemClickListener(listener);
    return this;
  }

  /**
   * Sets the ListView or GridView's item long click listener of the view
   *
   * @param viewId The view id.
   * @param listener The item long click listener;
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setOnItemLongClickListener(@IdRes int viewId,
      @NonNull AdapterView.OnItemLongClickListener listener) {
    ((AdapterView) retrieveView(viewId)).setOnItemLongClickListener(listener);
    return this;
  }

  /**
   * Sets the ListView or GridView's item selected click listener of the view
   *
   * @param viewId The view id.
   * @param listener The item selected click listener;
   * @return The AdapterViewHelper for chaining.
   */
  public AdapterViewHelper setOnItemSelectedClickListener(@IdRes int viewId,
      @NonNull AdapterView.OnItemSelectedListener listener) {
    ((AdapterView) retrieveView(viewId)).setOnItemSelectedListener(listener);
    return this;
  }

  @SuppressWarnings("unchecked")
  private <T extends View> T retrieveView(@IdRes int viewId) {
    View view = mViews.get(viewId);
    if (view == null) {
      view = mConvertView.findViewById(viewId);
      mViews.put(viewId, view);
    }
    return (T) view;
  }


  public Resources getResources(){
    return mContext.getResources();
  }

  protected String getString(int strId){
    return mContext.getResources().getString(strId);
  }

  protected String getString(int resId,Object ... obj){
   return  mContext.getResources().getString(resId,obj);
  }

  public AdapterViewHelper setClickable(int viewId, boolean b) {
    retrieveView(viewId).setClickable(b);
    return this;
  }

  /**
   *
   * @param mContext
   * @param colorId
   * @return
   */
  public static @ColorInt int getColor(Context mContext, int colorId) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      return mContext.getColor(colorId);
    }else{
      return mContext.getResources().getColor(colorId);
    }
  }

}
